%{
/*-
 * Copyright (c) 2010-2013 Kai Wang
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "ld.h"
#include "ld_script_parser.h"

ELFTC_VCSID("$Id: ld_script_lexer.l 2875 2013-01-09 22:46:03Z kaiwang27 $");

#define	YY_NO_UNPUT
int lineno = 1;

int	yylex(void);

static void	_calc_num(void);
static void	_get_string(void);
static void	_get_ident(void);
static void	_skip_comment(void);

%}

DEC		[0-9]+[kKmMdD]?
OCT		(0[0-7]*+[kKmM]?)|([0-7]+[oO])
HEX		(0[xX][0-9a-fA-F]+[kKmMhH]?)|([0-9a-fA-F]+[hH])
IDENT		[-_A-Za-z/:$.\\~][-_A-Za-z/:$.\\~0-9]*
WILDCARD	[-_A-Za-z/:$.\\~0-9\[\]?*]+
MATTR		\(!?[rRwWxXaAiIlL]+\)

%option noyywrap
%option never-interactive

%%

ABSOLUTE			{ return (T_ABSOLUTE); }
ADDR				{ return (T_ADDR); }
ALIGN				{ return (T_ALIGN); }
ALIGNOF				{ return (T_ALIGNOF); }
ASSERT				{ return (T_ASSERT); }
AS_NEEDED			{ return (T_AS_NEEDED); }
AT				{ return (T_AT); }
BIND				{ return (T_BIND); }
BLOCK				{ return (T_BLOCK); }
BYTE				{ return (T_BYTE); }
COMMONPAGESIZE			{ _get_ident(); return (T_COMMONPAGESIZE); }
CONSTANT			{ return (T_CONSTANT); }
CONSTRUCTORS			{ return (T_CONSTRUCTORS); }
COPY				{ return (T_COPY); }
CREATE_OBJECT_SYMBOLS		{ return (T_CREATE_OBJECT_SYMBOLS); }
DATA_SEGMENT_ALIGN		{ return (T_DATA_SEGMENT_ALIGN); }
DATA_SEGMENT_END		{ return (T_DATA_SEGMENT_END); }
DATA_SEGMENT_RELRO_END		{ return (T_DATA_SEGMENT_RELRO_END); }
DEFINED				{ return (T_DEFINED); }
DSECT				{ return (T_DSECT); }
ENTRY				{ return (T_ENTRY); }
EXCLUDE_FILE			{ return (T_EXCLUDE_FILE); }
EXTERN				{ return (T_EXTERN); }
FILEHDR				{ return (T_FILEHDR); }
FILL				{ return (T_FILL); }
FLAGS				{ return (T_FLAGS); }
FLOAT				{ return (T_FLOAT); }
FORCE_COMMON_ALLOCATION		{ return (T_FORCE_COMMON_ALLOCATION); }
GROUP				{ return (T_GROUP); }
HLL				{ return (T_HLL); }
INCLUDE				{ return (T_INCLUDE); }
INFO				{ return (T_INFO); }
INHIBIT_COMMON_ALLOCATION	{ return (T_INHIBIT_COMMON_ALLOCATION); }
INPUT				{ return (T_INPUT); }
KEEP				{ return (T_KEEP); }
LENGTH				{ return (T_LENGTH); }
LOADADDR			{ return (T_LOADADDR); }
LONG				{ return (T_LONG); }
MAP				{ return (T_MAP); }
MAX				{ return (T_MAX); }
MAXPAGESIZE			{ _get_ident(); return (T_MAXPAGESIZE); }
MEMORY				{ return (T_MEMORY); }
MIN				{ return (T_MIN); }
NEXT				{ return (T_NEXT); }
NOCROSSREFS			{ return (T_NOCROSSREFS); }
NOFLOAT				{ return (T_NOFLOAT); }
NOLOAD				{ return (T_NOLOAD); }
ONLY_IF_RO			{ return (T_ONLY_IF_RO); }
ONLY_IF_RW			{ return (T_ONLY_IF_RW); }
OPTION				{ return (T_OPTION); }
ORIGIN				{ return (T_ORIGIN); }
OUTPUT				{ return (T_OUTPUT); }
OUTPUT_ARCH			{ return (T_OUTPUT_ARCH); }
OUTPUT_FORMAT			{ return (T_OUTPUT_FORMAT); }
OVERLAY				{ return (T_OVERLAY); }
PHDRS				{ return (T_PHDRS); }
PROVIDE				{ return (T_PROVIDE); }
PROVIDE_HIDDEN			{ return (T_PROVIDE_HIDDEN); }
QUAD				{ return (T_QUAD); }
REGION_ALIAS			{ return (T_REGION_ALIAS); }
SEARCH_DIR			{ return (T_SEARCH_DIR); }
SECTIONS			{ return (T_SECTIONS); }
SEGMENT_START			{ return (T_SEGMENT_START); }
SHORT				{ return (T_SHORT); }
SIZEOF				{ return (T_SIZEOF); }
SIZEOF_HEADERS			{ return (T_SIZEOF_HEADERS); }
SORT				{ return (T_SORT_BY_NAME); }
SORT_BY_ALIGNMENT		{ return (T_SORT_BY_ALIGNMENT); }
SORT_BY_NAME			{ return (T_SORT_BY_NAME); }
SPECIAL				{ return (T_SPECIAL); }
SQUAD				{ return (T_SQUAD); }
STARTUP				{ return (T_STARTUP); }
SUBALIGN			{ return (T_SUBALIGN); }
SYSLIB				{ return (T_SYSLIB); }
TARGET				{ return (T_TARGET); }
TRUNCATE			{ return (T_TRUNCATE); }
extern				{ return (T_VER_EXTERN); }
global:				{ return (T_VER_GLOBAL); }
l				{ return (T_LENGTH); }
len				{ return (T_LENGTH); }
local:				{ return (T_VER_LOCAL); }
o				{ return (T_ORIGIN); }
org				{ return (T_ORIGIN); }
sizeof_headers			{ return (T_SIZEOF_HEADERS); }
"/*"				{ _skip_comment(); }
"\""[^\"]+"\""			{ _get_string(); return (T_STRING); }
{DEC}|{OCT}|{HEX}		{ _calc_num(); return (T_NUM); }
"<<="				{ return (T_LSHIFT_E); }
">>="				{ return (T_RSHIFT_E); }
"<<"				{ return (T_LSHIFT); }
">>"				{ return (T_RSHIFT); }
"=="				{ return (T_EQ); }
"!="				{ return (T_NE); }
">="				{ return (T_GE); }
"<="				{ return (T_LE); }
"+="				{ return (T_ADD_E); }
"-="				{ return (T_SUB_E); }
"*="				{ return (T_MUL_E); }
"/="				{ return (T_DIV_E); }
"&="				{ return (T_AND_E); }
"|="				{ return (T_OR_E); }
"&&"				{ return (T_LOGICAL_AND); }
"||"				{ return (T_LOGICAL_OR); }
"!"				{ return ('!'); }
"{"				{ return ('{'); }
"}"				{ return ('}'); }
"["				{ return ('['); }
"]"				{ return (']'); }
"("				{ return ('('); }
")"				{ return (')'); }
"?"				{ return ('?'); }
":"				{ return (':'); }
";"				{ return (';'); }
"&"				{ return ('&'); }
"|"				{ return ('|'); }
"~"				{ return ('~'); }
"+"				{ return ('+'); }
"-"				{ return ('-'); }
"*"				{ return ('*'); }
"/"				{ return ('/'); }
"%"				{ return ('%'); }
"="				{ return ('='); }
"<"				{ return ('<'); }
">"				{ return ('>'); }
","				{ return (','); }
"."				{ return ('.'); }
{MATTR}				{ _get_ident(); return (T_MEMORY_ATTR); }
{IDENT}				{ _get_ident(); return (T_IDENT); }
{WILDCARD}			{ _get_ident(); return (T_WILDCARD); }
"\n"				{ lineno++; }
[ \t]				/* Ignore whitespaces. */

%%

static void
_calc_num(void)
{
	int base, mul;

	base = 0;
	mul = 1;
	switch (yytext[yyleng - 1]) {
	case 'd': case 'D':
		base = 10;
		break;
	case 'o': case 'O':
		base = 8;
		break;
	case 'h': case 'H':
		base = 16;
		break;
	case 'k': case 'K':
		mul = 1024;
		break;
	case 'm': case 'M':
		mul = 1024 * 1024;
		break;
	default:
		break;
	}

	if (base || mul > 1)
		yytext[yyleng - 1] = '\0';

	yylval.num = strtoimax(yytext, NULL, base);
	yylval.num *= mul;
}

static void
_get_ident(void)
{

	yylval.str = strdup(yytext);
	if (yylval.str == NULL)
		err(1, "strdup");
}

static void
_get_string(void)
{

	yytext[yyleng - 1] = '\0';
	yylval.str = strdup(yytext + 1);
	if (yylval.str == NULL)
		err(1, "strdup");
}

static void
_skip_comment(void)
{
	int c;

	for (;;) {
		while ((c = input()) != '*' && c != EOF)
			if (c == '\n')
				lineno++;
		if (c == '*') {
			while ((c = input()) == '*')
				;
			if (c == '\n')
				lineno++;
			if (c == '/')
				break;
		}
		if (c == EOF)
			errx(1, "lexer: EOF in comment");
	}
}
